package principal;

import edu.uci.ics.jung.algorithms.layout.*;
import edu.uci.ics.jung.algorithms.shortestpath.DijkstraShortestPath;
import edu.uci.ics.jung.graph.DirectedGraph;
import edu.uci.ics.jung.graph.DirectedOrderedSparseMultigraph;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
import edu.uci.ics.jung.visualization.control.PickingGraphMousePlugin;
import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.swing.*;
import model.Aluno;
import org.apache.commons.collections15.Transformer;
import org.apache.commons.collections15.functors.ChainedTransformer;

public class Grafo {
    
    private Integer distancia = 0;
    
    public DirectedGraph<String, String> criar_grafo(boolean isRM, ArrayList<Aluno> alunos) {
        DirectedGraph<String, String> g = new DirectedOrderedSparseMultigraph<String, String>();
        
        for(Aluno a : alunos) {
            if(isRM) {
                g.addVertex("RM " + String.valueOf(a.getRm()));
            }
            else {
                g.addVertex(a.getNome());
            }
        }
        
        int index = 1;
        for(Aluno a : alunos) {
            Integer[] conexoes_rm = a.getConexoes_rm();
            String[] conexoes_nome = a.getConexoes_nome();
            for(int cont = 0 ; cont < conexoes_rm.length ; cont++) {
                if(conexoes_rm[cont] != 0) {
                    if(isRM) {
                        String nome_edge = "Edge RM " + String.valueOf(a.getRm()) + " - " + "RM " + String.valueOf(conexoes_rm[cont]);
                        g.addEdge(nome_edge, "RM " + String.valueOf(a.getRm()), "RM " + String.valueOf(conexoes_rm[cont]));
                    }
                    else {
                        String nome_edge = "Edge " + a.getNome() + " - " + conexoes_nome[cont];
                        g.addEdge(nome_edge, a.getNome(), conexoes_nome[cont]);
                    }
                    index++;
                }
            }
        }
        
    return(g);
    }
    
    public boolean exibir_conexoes_proximas(boolean isRM, Aluno aluno, ArrayList<Aluno> alunos) {
        DirectedGraph<String, String> g = new DirectedOrderedSparseMultigraph<String, String>();
        
        Integer[] conexoes_rm = aluno.getConexoes_rm();
        ArrayList<Aluno> alunos_proximos = new ArrayList<Aluno>();
        alunos_proximos.add(aluno);
        for(Integer i : conexoes_rm) {
            if(i != 0) {
                for(Aluno a : alunos) {
                    if(a.getRm() - i == 0) {
                        alunos_proximos.add(a);
                    }
                }
            }
        }
        if(alunos_proximos.size() <= 1) {
            return(false);
        }
        
        for(Aluno a : alunos_proximos) {
            if(isRM) {
                g.addVertex("RM " + String.valueOf(a.getRm()));
            }
            else {
                g.addVertex(a.getNome());
            }
        }
        
        int index = 1;
        for(Aluno a : alunos_proximos) {
            for(Aluno b : alunos_proximos) {
                conexoes_rm = b.getConexoes_rm();
                for(Integer i : conexoes_rm) {
                    if(i != 0) {
                        if(a.getRm() - i == 0) {
                            String edge = "Edge " + String.valueOf(index);
                            if(isRM) {
                                g.addEdge(edge, "RM " + String.valueOf(b.getRm()), "RM " + String.valueOf(a.getRm()));
                            }
                            else {
                                g.addEdge(edge, b.getNome(), a.getNome());
                            }
                            
                            index++;
                        }
                    }
                }
            }
        }
        
        Layout<String, String> layout = new KKLayout(g);
        
        if(isRM) {
            exibir(layout, "Conexões Próximas: " + aluno.getRm(), 500, 400);    
        }
        else {
            exibir(layout, "Conexões Próximas: " + aluno.getNome(), 500, 400);
        }
        
    return(true);
    }
    
    public boolean exibir_menor_caminho(Boolean grafo_rm, Aluno aluno1, Aluno aluno2, DirectedGraph<String, String> g) {
        String origem = aluno1.getNome();
        String destino = aluno2.getNome();
        
        if(grafo_rm || aluno1.getNome().equals("") || aluno1.getNome().substring(0, 2).equals("RM") || isRM(aluno1.getNome())) {
            origem = "RM " + String.valueOf(aluno1.getRm());
        }
        if(grafo_rm || aluno2.getNome().equals("") || aluno2.getNome().substring(0, 2).equals("RM") || isRM(aluno2.getNome())) {
            destino = "RM " + String.valueOf(aluno2.getRm());
        }
        
        DijkstraShortestPath<String, String> shortest = new DijkstraShortestPath<String, String>(g);
        
        shortest.setMaxDistance(20);
        
        List<String> paths = shortest.getPath(origem, destino);
        
        DirectedGraph<String, String> caminho = new DirectedOrderedSparseMultigraph<String, String>();
        
        if(!paths.isEmpty()) {
            distancia = shortest.getDistance(origem, destino).intValue();
        }
        
        System.out.println("\n\nShortest Path:");
        for (String s : paths) {
            for(int cont = 0 ; cont < s.length() ; cont++) {
                if(s.charAt(cont) == '-') {
                    origem = s.substring(5, cont-1);
                    destino = s.substring(cont+2, s.length());
                    caminho.addVertex(origem);
                    caminho.addVertex(destino);
                    caminho.addEdge("Edge " + origem + " - " + destino, origem, destino);
                    System.out.println(origem + " ---> " + destino);
                }
            }
        }
        
        if(paths.isEmpty()) {
            return(false);
        }
        
        Layout<String, String> layout = new KKLayout(caminho);
        

        exibir(layout, "Menor Caminho", 500, 400);    
        
    return(true);
    }
    
    public ArrayList<String> listar_nao_alcancaveis(boolean grafo_rm, Aluno aluno, DirectedGraph<String, String> g, ArrayList<Aluno> alunos) {
        DijkstraShortestPath<String, String> shortest = new DijkstraShortestPath<String, String>(g);
        shortest.setMaxDistance(20);
        
        String origem = aluno.getNome();
        
        boolean use_rm = false;
        
        if(grafo_rm || aluno.getNome().equals("") || aluno.getNome().substring(0, 2).equals("RM") || isRM(aluno.getNome())) {
            origem = "RM " + String.valueOf(aluno.getRm());
            use_rm = true;
        }

        Map<String, Number> mapa = shortest.getDistanceMap(origem);
        
        List<String> lista1 = new ArrayList<String>(mapa.keySet());
        List<Number> lista2 = new ArrayList<Number>(mapa.values());
        
        System.out.println("\n# Aluno | Distância: #\n");
        
        for(int cont = 0 ; cont < lista1.size() && cont < lista2.size() ; cont++) {
            System.out.println(lista1.get(cont) + " | " + lista2.get(cont));
        }
                
        ArrayList<String> nao_alcancaveis = new ArrayList<String>();
        
        System.out.println("\n# Não Alcançáveis: #\n");
        for(Aluno a : alunos) {
            if(use_rm) {
                origem = "RM " + a.getRm();
            }
            else {
                origem = a.getNome();
            }
            boolean achou = false;
            for(String s : lista1) {
                if(origem.equals(s)) {
                    achou = true;
                }
            }
            if(!achou) {
                System.out.println(origem);
                nao_alcancaveis.add(origem);
            }
            
        }
        
    return(nao_alcancaveis);
    }
    
    public ArrayList<String> listar_qtde_maxima(boolean grafo_rm, Aluno aluno, DirectedGraph<String, String> g, ArrayList<Aluno> alunos, Integer distancia) {
        DijkstraShortestPath<String, String> shortest = new DijkstraShortestPath<String, String>(g);
        shortest.setMaxDistance(distancia);
        
        String origem = aluno.getNome();
        
        boolean use_rm = false;
        
        if(grafo_rm || aluno.getNome().equals("") || aluno.getNome().substring(0, 2).equals("RM") || isRM(aluno.getNome())) {
            origem = "RM " + String.valueOf(aluno.getRm());
            use_rm = true;
        }
        
        Map<String, Number> mapa = shortest.getDistanceMap(origem);
        
        List<String> lista1 = new ArrayList<String>(mapa.keySet());
        List<Number> lista2 = new ArrayList<Number>(mapa.values());
        
        System.out.println("\n# Aluno | Distância: #\n");
        
        for(int cont = 0 ; cont < lista1.size() && cont < lista2.size() ; cont++) {
            System.out.println(lista1.get(cont) + " | " + lista2.get(cont));
        }
        
        ArrayList<String> aluno_distancia = new ArrayList<String>();
        
        for(int cont = 0 ; cont < lista1.size() && cont < lista2.size() ; cont++) {
            if(lista2.get(cont).intValue() - 0 != 0) {
                aluno_distancia.add(lista1.get(cont) + " | " + lista2.get(cont));
            }
        }
        
        
    return(aluno_distancia);
    }
    
    public void exibir_grafo(DirectedGraph<String, String> g, String layout_tipo, Integer tamx, Integer tamy) {
        if(g != null) {
            Layout<Integer, String> layout;
            if("CircleLayout".equals(layout_tipo)) {
                layout = new CircleLayout(g);
            }
            else if("FRLayout".equals(layout_tipo)) {
                layout = new FRLayout(g);
            }
            else if("FRLayout2".equals(layout_tipo)) {
                layout = new FRLayout2(g);
            }
            else if("ISOMLayout".equals(layout_tipo)) {
                layout = new ISOMLayout(g);
            }
            else if("SpringLayout".equals(layout_tipo)) {
                layout = new edu.uci.ics.jung.algorithms.layout.SpringLayout(g);
            }
            else if("SpringLayout2".equals(layout_tipo)) {
                layout = new SpringLayout2(g);
            }
            else {
                layout_tipo = "KKLayout";
                layout = new KKLayout(g);
            }
            
            System.out.println("\n\nUsando: " + layout_tipo);
            exibir(layout, layout_tipo, tamx, tamy);
            
        }
    }
   
    
    private void exibir(Layout layout, String layout_tipo, Integer tamx, Integer tamy) {
        layout.setSize(new Dimension((tamx - 50),(tamy - 50)));
        
        VisualizationViewer<String, String> vv = new VisualizationViewer<String, String>(layout);
        vv.setPreferredSize(new Dimension(tamx,tamy));

        // Cor de fundo: Branca:
        vv.setBackground(Color.white);
        
        // Exibe os nomes nos vértices em negrito:
        Transformer labelTransformer = new ChainedTransformer<String,String>(new Transformer[]{
            new ToStringLabeller<String>(),
            new Transformer<String,String>() {
            public String transform(String input) {
                return "<html><b>"+input;
            }}});
        
        vv.getRenderContext().setVertexLabelTransformer(labelTransformer);
        
        // Exibe o nome do vértice quando o mouse estiver em cima dele:
        vv.setVertexToolTipTransformer(vv.getRenderContext().getVertexLabelTransformer());
        

        // Função de Movimentar Vértices:
        DefaultModalGraphMouse graph_mouse = new DefaultModalGraphMouse() {
            protected void loadPlugins() {}
        };
        
        graph_mouse.add(new PickingGraphMousePlugin());
        vv.setGraphMouse(graph_mouse);
        
        final JFrame frame = new JFrame();
        frame.setTitle(layout_tipo);
        frame.setResizable(false);
        frame.getContentPane().add(vv);
        frame.pack();
        frame.setVisible(true);
        frame.setLocationRelativeTo(null);
        
        
        // ESC Key:
        KeyStroke escapeKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false);
        Action escapeAction = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
               frame.dispose();
            }  
        };
        
        frame.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(escapeKeyStroke, "ESCAPE");
        frame.getRootPane().getActionMap().put("ESCAPE", escapeAction);
    }

    public Integer getDistancia() {
        return distancia;
    }
    
    private boolean isRM(String aluno) {
        try {
            Integer rm = Integer.parseInt(aluno);
        }
        catch(NumberFormatException e) {
            return(false);
        }
        
    return(true);
    }
    
}
